import axios from "axios";
import AxiosCancel from "@/api/request/axiosCancel";
import {cloneDeep} from "lodash-es";
import {isFunction} from "@/utils";

class CustomRequest {
  constructor(options) {
    this.options = options;
    this.axiosInstance = axios.create(options)
    this.setupInterceptors();
  }

  getAxiosInstance() {
    return this.axiosInstance;
  }

  /**
   * @description 重新配置axios
   * @param config
   */
  configAxios(config) {
    if (!this.axiosInstance) {
      return;
    }
    this.createAxios(config)
  }

  /**
   * @description 设置通用header
   * @param headers
   */
  setHeaders(headers) {
    if (!this.axiosInstance) {
      return
    }
    Object.assign(this.axiosInstance.defaults.headers, headers)
  }

  /**
   * @description 创建axios实例
   * @param config
   */
  createAxios(config) {
    this.axiosInstance = axios.create(config);
  }

  getTransform() {
    const {transform} = this.options;
    return transform;
  }

  setupInterceptors() {
    const transform = this.getTransform();
    if (!transform) {
      return
    }
    const {
      requestInterceptors,
      requestInterceptorsCatch,
      responseInterceptors,
      responseInterceptorsCatch
    } = transform;
    const axiosCanceler = new AxiosCancel();

    // 请求拦截处理
    this.axiosInstance.interceptors.request.use((config) => {
      const {headers: {ignoreCancelToken}} = config;
      let conf = cloneDeep(config);
      const ignoreCancel = ignoreCancelToken !== undefined ? ignoreCancelToken : this.options.requestOptions?.ignoreCancelToken;
      // 加入拦截
      !ignoreCancel && axiosCanceler.addPending(config);
      if (requestInterceptors && isFunction(requestInterceptors)) {
        conf = requestInterceptors(config, this.options);
      }
      return conf;
    }, (error) => {
      if (requestInterceptorsCatch && isFunction(requestInterceptorsCatch)) {
        requestInterceptorsCatch(error)
        return;
      }
      return Promise.reject(error);
    })

    // 响应拦截处理
    this.axiosInstance.interceptors.response.use((res) => {
      // 清除拦截
      res && axiosCanceler.removePending(res.config)
      if (responseInterceptors && isFunction(responseInterceptors)) {
        res = responseInterceptors(res)
      }
      return res;
    }, (error) => {
      if (responseInterceptors && isFunction(responseInterceptorsCatch)) {
        responseInterceptorsCatch(error)
      }
      return Promise.reject(error)
    })
  }

  request(config, options) {
    let conf = cloneDeep(config);
    const transform = this.getTransform();
    const {requestOptions = {}} = this.options;
    const opt = Object.assign({}, requestOptions, options ? options : {})
    const {beforeRequestHook, requestCatch, transformRequestData} = transform || {}
    if (beforeRequestHook && isFunction(beforeRequestHook)) {
      conf = beforeRequestHook(conf, opt)
    }

    // 这里重置为最新的requestOptions
    conf.requestOptions = opt;
    return new Promise((resolve, reject) => {
      this.axiosInstance.request(conf).then((res) => {
        // 请求是否被取消
        const isCancel = axios.isCancel(res)
        if (transformRequestData && isFunction(transformRequestData) && !isCancel) {
          try {
            const ret = transformRequestData(res, opt)
            resolve(ret)
          } catch (error) {
            reject(error || new Error('request error!'))
          }
          return;
        }
        resolve(res)
      }, (error) => {
        if (requestCatch && isFunction(requestCatch)) {
          reject(requestCatch(error))
          return;
        }
        reject(error)
      })
    })
  }

  get(config, options) {
    return this.request({...config, method: 'get'}, options);
  }

  post(config, options) {
    return this.request({...config, method: 'post'}, options);
  }

  head(config, options) {
    return this.request({...config, method: 'head'}, options);
  }

  put(config, options) {
    return this.request({...config, method: 'put'}, options);
  }

  delete(config, options) {
    return this.request({...config, method: 'delete'}, options);
  }

  options(config, options) {
    return this.request({...config, method: 'options'}, options);
  }

  connect(config, options) {
    return this.request({...config, method: 'connect'}, options);
  }

  trace(config, options) {
    return this.request({...config, method: 'trace'}, options);
  }
}

export default CustomRequest;
